package com.unionpay.acp.sdk;

import cn.demoncat.util.exception.BizRuntimeException;
import cn.demoncat.util.io.CloseUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.ByteArrayInputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.security.KeyStore;
import java.security.PrivateKey;
import java.security.PublicKey;
import java.security.Security;
import java.security.cert.*;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

import static com.unionpay.acp.sdk.SDKConstants.UNIONPAY_CNNAME;

/**
 * 证书工具类
 * 
 * @author 银联
 *
 * @since 2016-7-22
 */
public class CertUtil {
	
	/** 证书容器，存储对商户请求报文签名私钥证书. */
	private static KeyStore keyStore = null;
	/** 敏感信息加密公钥证书 */
	private static X509Certificate encryptCert = null;
	/** 验签中级证书 */
	private static X509Certificate middleCert = null;
	/** 验签根证书 */
	private static X509Certificate rootCert = null;
	/** 商户私钥存储Map */
	private final static Map<String, KeyStore> keyStoreMap = new ConcurrentHashMap<>();
	
	private static final Logger LOG = LoggerFactory.getLogger(CertUtil.class);
	
	/**
	 * 初始化所有证书
	 * 
	 * @author 延晓磊
	 *
	 * @since 2020年3月26日
	 */
	public static void init() {
		try {
			// 向系统添加BC provider
			addProvider();
			// 初始化签名私钥证书
			initSignCert();
			// 初始化验签证书的中级证书
			initMiddleCert();
			// 初始化验签证书的根证书
			initRootCert();
			// 初始化加密公钥
			initEncryptCert();
		} catch (Exception e) {
			throw buildException(e);
		}
	}
	
	/**
	 * 加载算法提供者
	 * 
	 * @author 延晓磊
	 *
	 * @since 2020年3月26日
	 */
	private static void addProvider(){
		if (Security.getProvider("BC") == null) {
			Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
		} else {
			// 解决eclipse调试时tomcat自动重新加载时，BC存在不明原因异常的问题。
			Security.removeProvider("BC"); 
			Security.addProvider(new org.bouncycastle.jce.provider.BouncyCastleProvider());
		}
	}
	
	/**
	 * 加载签名证书
	 * 
	 * @author 延晓磊
	 *
	 * @since 2020年3月26日
	 */
	private static void initSignCert() {
		keyStore = getKeyInfo(SDKConfig.signCertPath, SDKConfig.signCertPwd, SDKConfig.signCertType);
	}

	/**
	 * 加载中级证书
	 * 
	 * @author 延晓磊
	 *
	 * @since 2020年3月26日
	 */
	private static void initMiddleCert() {
		middleCert = initCert(SDKConfig.middleCertPath);
	}

	/**
	 * 加载根证书
	 * 
	 * @author 延晓磊
	 *
	 * @since 2020年3月26日
	 */
	private static void initRootCert() {
		rootCert = initCert(SDKConfig.rootCertPath);
	}
	
	/**
	 * 加载敏感信息加密证书
	 * 
	 * @author 延晓磊
	 *
	 * @since 2020年3月26日
	 */
	private static void initEncryptCert() {
		encryptCert = initCert(SDKConfig.encryptCertPath);
	}

	/**
	 * 加载签名证书，并保存到certKeyStoreMap
	 * 
	 * @param certFilePath
	 * @param certPwd
	 * 
	 * @author 延晓磊
	 *
	 * @since 2020年3月26日
	 */
	private static void loadSignCert(String certFilePath, String certPwd) {
		KeyStore keyStore = getKeyInfo(certFilePath, certPwd, SDKConfig.signCertType);
		keyStoreMap.put(certFilePath, keyStore);
	}

	/**
	 * 初始化为公钥证书
	 * 
	 * @param path 证书路径
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2020年3月26日
	 */
	private static X509Certificate initCert(String path) {
		FileInputStream in = null;
		try {
			CertificateFactory cf = CertificateFactory.getInstance("X.509", "BC");
			in = new FileInputStream(path);
			return (X509Certificate) cf.generateCertificate(in);
		} catch (Exception e) {
			throw buildException(e);
		} finally {
			CloseUtil.close(in);
		}
	}
	
	/**
	 * 通过keyStore 获取私钥签名证书PrivateKey对象
	 * 
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2020年3月26日
	 */
	public static PrivateKey getSignCertPrivateKey() {
		try {
			Enumeration<String> aliasenum = keyStore.aliases();
			String keyAlias = null;
			if (aliasenum.hasMoreElements()) {
				keyAlias = aliasenum.nextElement();
			}
			return (PrivateKey) keyStore.getKey(keyAlias, SDKConfig.signCertPwd.toCharArray());
		} catch (Exception e) {
			throw buildException(e);
		}
	}
	
	/**
	 * 通过指定路径的私钥证书  获取PrivateKey对象
	 * 
	 * @param certPath
	 * @param certPwd
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2020年3月26日
	 */
	public static PrivateKey getSignCertPrivateKeyByStoreMap(String certPath, String certPwd) {
		if (!keyStoreMap.containsKey(certPath)) {
			loadSignCert(certPath, certPwd);
		}
		try {
			Enumeration<String> aliasenum = keyStoreMap.get(certPath).aliases();
			String keyAlias = null;
			if (aliasenum.hasMoreElements()) {
				keyAlias = aliasenum.nextElement();
			}
			return (PrivateKey) keyStoreMap.get(certPath).getKey(keyAlias, certPwd.toCharArray());
		} catch (Exception e) {
			throw buildException(e);
		}
	}

	/**
	 * 获取敏感信息加密证书PublicKey
	 * 
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2020年3月26日
	 */
	public static PublicKey getEncryptCertPublicKey() {
		if (null == encryptCert) {
			encryptCert = initCert(SDKConfig.encryptCertPath);
		}
		return encryptCert.getPublicKey();
	}
	
	/**
	 * 重置敏感信息加密证书公钥
	 * 
	 * @author 延晓磊
	 *
	 * @since 2020年3月26日
	 */
	public static void resetEncryptCertPublicKey() {
		encryptCert = null;
	}
	
	/**
	 * 获取签名私钥证书certId
	 * 
	 * @return 证书编号
	 * 
	 * @author 延晓磊
	 *
	 * @since 2020年3月26日
	 */
	public static String getSignCertId() {
		try {
			Enumeration<String> aliasenum = keyStore.aliases();
			String keyAlias = null;
			if (aliasenum.hasMoreElements()) {
				keyAlias = aliasenum.nextElement();
			}
			X509Certificate cert = (X509Certificate) keyStore.getCertificate(keyAlias);
			return cert.getSerialNumber().toString();
		} catch (Exception e) {
			throw buildException(e);
		}
	}

	/**
	 * 获取敏感信息加密证书的certId
	 * 
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2020年3月26日
	 */
	public static String getEncryptCertId() {
		if (null == encryptCert) {
			encryptCert = initCert(SDKConfig.encryptCertPath);
		}
		return encryptCert.getSerialNumber().toString();
	}

	/**
	 * 将签名私钥证书文件读取为证书存储对象
	 * 
	 * @param pfxkeyfile
	 *            证书文件名
	 * @param keypwd
	 *            证书密码
	 * @param type
	 *            证书类型
	 *            
	 * @return 证书对象
	 * 
	 * @throws IOException 
	 */
	private static KeyStore getKeyInfo(String pfxkeyfile, String keypwd, String type){
		FileInputStream fis = null;
		try {
			KeyStore ks = KeyStore.getInstance(type, "BC");
			fis = new FileInputStream(pfxkeyfile);
			if (null != ks) {
				ks.load(fis, keypwd.toCharArray());
			}
			return ks;
		} catch (Exception e) {
			throw buildException(e);
		} finally {
			CloseUtil.close(fis);
		}
	}
	
	/**
	 * 将字符串转换为X509Certificate对象.
	 * 
	 * @param x509CertString
	 * 
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2020年3月26日
	 */
	public static X509Certificate genCertificateByStr(String x509CertString) {
		try {
			CertificateFactory cf = CertificateFactory.getInstance("X.509", "BC"); 
			InputStream tIn = new ByteArrayInputStream(x509CertString.getBytes(StandardCharsets.ISO_8859_1));
			return (X509Certificate) cf.generateCertificate(tIn);
		} catch (Exception e) {
			throw buildException(e);
		}
	}
	
	/**
	 * 获取验签公钥使用的中级证书
	 * 
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2020年3月26日
	 */
	public static X509Certificate getMiddleCert() {
		if (null == middleCert) {
			initMiddleCert();
		}
		return middleCert;
	}
	
	/**
	 * 获取验签公钥使用的根证书
	 * 
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2020年3月26日
	 */
	public static X509Certificate getRootCert() {
		if (null == rootCert) {
			initRootCert();
		}
		return rootCert;
	}

	/**
	 * 获取证书的CN
	 * 
	 * @param aCert
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2020年3月26日
	 */
	private static String getIdentitiesFromCertficate(X509Certificate aCert) {
		String tDN = aCert.getSubjectDN().toString(); 
		String tPart = "";
		if ((tDN != null)) {
			String[] tSplitStr = tDN.substring(tDN.indexOf("CN=")).split("@");
			if (tSplitStr != null && tSplitStr.length > 2 && tSplitStr[2] != null) {
				tPart = tSplitStr[2];
			}
		}
		return tPart;
	}
	
	/**
	 * 校验证书链
	 * 
	 * @param cert
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2020年3月26日
	 */
	private static boolean verifyCertificateChain(X509Certificate cert){
		if (null == cert) {
			LOG.error("cert must Not null");
			return false;
		}
		// 中级证书
		X509Certificate middleCert = CertUtil.getMiddleCert();
		if (null == middleCert) {
			LOG.error("middleCert must Not null");
			return false;
		}
		// 根证书
		X509Certificate rootCert = CertUtil.getRootCert();
		if (null == rootCert) {
			LOG.error("rootCert or cert must Not null");
			return false;
		}
		
		try {
			// 校验
	        X509CertSelector selector = new X509CertSelector();
	        selector.setCertificate(cert);
	        Set<TrustAnchor> trustAnchors = new HashSet<>();
	        trustAnchors.add(new TrustAnchor(rootCert, null));
	        PKIXBuilderParameters pkixParams = new PKIXBuilderParameters(trustAnchors, selector);
	        Set<X509Certificate> intermediateCerts = new HashSet<>();
	        intermediateCerts.add(rootCert);
	        intermediateCerts.add(middleCert);
	        intermediateCerts.add(cert);
	        pkixParams.setRevocationEnabled(false);
	        CertStore intermediateCertStore = CertStore.getInstance("Collection", new CollectionCertStoreParameters(intermediateCerts), "BC");
	        pkixParams.addCertStore(intermediateCertStore);
	        CertPathBuilder builder = CertPathBuilder.getInstance("PKIX", "BC");
        	@SuppressWarnings("unused")
			PKIXCertPathBuilderResult result = (PKIXCertPathBuilderResult) builder.build(pkixParams);
			return true;
        } catch (Exception e){
        	LOG.error("verify certificate chain error", e);
		}
		return false;
	}
	
	/**
	 * 检查证书链
	 * 
	 * @param cert
	 *            待验证的证书
	 * @return
	 */
	public static boolean verifyCertificate(X509Certificate cert) {
		if (null == cert) {
			LOG.error("cert must Not null");
			return false;
		}
		try {
			// 验证有效期
			cert.checkValidity();
			// 验证证书链
			if(!verifyCertificateChain(cert)){
				return false;
			}
		} catch (Exception e) {
			LOG.error("verifyCertificate fail", e);
			return false;
		}
		
		if(SDKConfig.ifValidateCNName){
			// 验证公钥是否属于银联
			if(!UNIONPAY_CNNAME.equals(CertUtil.getIdentitiesFromCertficate(cert))) {
				LOG.warn("cer owner is not CUP:" + CertUtil.getIdentitiesFromCertficate(cert));
				return false;
			}
		} else {
			// 验证公钥是否属于银联
			if(!UNIONPAY_CNNAME.equals(CertUtil.getIdentitiesFromCertficate(cert)) && !"00040000:SIGN".equals(CertUtil.getIdentitiesFromCertficate(cert))) {
				LOG.warn("cer owner is not CUP:" + CertUtil.getIdentitiesFromCertficate(cert));
				return false;
			}
		}
		return true;		
	}



	/**
	 * 构建异常
	 * 
	 * @param e
	 * @return
	 * 
	 * @author 延晓磊
	 *
	 * @since 2020年3月26日
	 */
	private static BizRuntimeException buildException(Exception e) {
		return new BizRuntimeException("加载支付证书失败", e);
	}
}
